package org.ccay.excel.importer.service.impl.internal;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.ccay.async.task.ImportTaskVO;
import org.ccay.async.task.service.IImportTaskService;
import org.ccay.core.exception.ApplicationException;
import org.ccay.core.io.FileStoreType;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.IUserPrincipal;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.util.CollectionUtil;
import org.ccay.core.util.PathUtil;
import org.ccay.excel.importer.ExcelCellError;
import org.ccay.excel.importer.ExcelImportConstants;
import org.ccay.excel.importer.ExcelImportRequest;
import org.ccay.excel.importer.ICellErrorAppender;
import org.ccay.excel.importer.IExcelImportContext;
import org.ccay.excel.importer.IMonitor;
import org.ccay.excel.meta.ExcelMeta;
import org.ccay.excel.util.impl.ExcelUtil;
//import org.ccay.mail.MailVO;
//import org.ccay.mail.service.IMailService;
import org.ccay.message.PushMessageVO;
import org.ccay.message.service.IPushMessageService;
import org.ccay.registry.util.RegistryQueryUtil;
import org.ccay.vfs.ApacheVFSFileObject;
import org.dom4j.DocumentException;
import org.xml.sax.SAXException;

/**
 * Excel文件导入监视器	
 * @author chaowangbang
 * @since Mar 15, 2012
 */
public class ImportMonitor implements IMonitor {
	
	/** 日志记录器 **/
	private ILogger logger = CcayLoggerFactory.getLogger(ImportMonitor.class);
	
	/** Excel导入请求 **/
	private ExcelImportRequest request;
	
	/** 任务管理服务 **/
	private IImportTaskService importTaskService;
	
//	/** 邮件服务 **/
//	private IMailService defaultMailService;
	
	/** Excel导入上下文 **/
	private IExcelImportContext context;
	
	/**
	 * 任务开始的时间
	 */
	private long beginTime;
	
	/** 消息推送服务 **/
	private IPushMessageService pushMessageService;
	
	private static final String BASE_PATH = "Ccay.Excel.SendMail";
	
	private static final String BASE_PATH_EXCEL="Ccay.Excel.errorDisplay";
	
	/**
	 * 回写错误数据同步
	 */
	private static final String ERROR_File = "Synchronized";
	
	
	/**
	 * 构造函数
	 * @param context
	 */
	public ImportMonitor(ExcelImportRequest request) {
		this.request = request;
	}
	
	public void setImportTaskService(IImportTaskService importTaskService) {
		this.importTaskService = importTaskService;
	}
	
	public void setImportContext(IExcelImportContext context) {
		this.context = context;
	}
	
//	public void setDefaultMailService(IMailService defaultMailService) {
//		this.defaultMailService = defaultMailService;
//	}

	
	public void setPushMessageService(IPushMessageService pushMessageService) {
		this.pushMessageService = pushMessageService;
	}
	
	/** 开始excel导入任务	
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 */
	public void begin() throws ApplicationException {
		// 记录任务开始运行的时间
		this.beginTime = System.currentTimeMillis();
		
		// 更新任务信息
		int taskId = request.getTaskId();
		ImportTaskVO task = this.importTaskService.findImportTaskById(taskId);
		task.setImportStatus(ExcelImportConstants.STATUS_IMPORTING);
		task.setRunStartTime(new Date(this.beginTime));
		task.setErrorMsg("");//清楚错误信息
		this.importTaskService.updateImportTask(task);
	}
	
	/** excel导入任务顺利结束
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @throws ApplicationException
	 */
	public void end() throws ApplicationException {
		ImportTaskVO task = null;
		//如果导入数据和操作数据相同 那么成功
		if(this.context.getProcessedCount() == this.context.getImportedCount()){
			task = buildEndTask(ExcelImportConstants.STATUS_IMPORTED);
			//如果导入数据等于0 二操作数据大于0 那么失败
		}else if(this.context.getImportedCount() == 0 ){
			task = buildEndTask(ExcelImportConstants.STATUS_EXCEPTION);
		}else{
			//否则  就是部分成功
			task = buildEndTask(ExcelImportConstants.STATUS_PARTIAL_IMPORTED);
		}
		task.setOperateQty(this.context.getProcessedCount());
		task.setImportedQty(this.context.getImportedCount());
		
		// 将验证的错误信息写入文件
		errorFileExcelCol(task);
		
		this.importTaskService.updateImportTask(task);
		sendMessage();
	}
	
	
	/** excel导入任务异常结束	
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @param t
	 * @throws ApplicationException
	 */
	public void fail(Throwable t) throws ApplicationException {
		ImportTaskVO task = null;
		if(this.context.getImportedCount() > 0){
			//否则  就是部分成功
			task = buildEndTask(ExcelImportConstants.STATUS_PARTIAL_IMPORTED);
		}else{
			task = buildEndTask(ExcelImportConstants.STATUS_EXCEPTION);
		}
		task.setErrorMsg(ExcelUtil.buildErrorMsg4Security(t));
		// 将验证的错误信息写入文件
		errorFileExcelCol(task);
		this.importTaskService.updateImportTask(task);
		sendMessage();
	}
	
	/** 由于验证错误引起的导入任务中断	
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @param t
	 * @throws ApplicationException
	 */
	public void fail4Validation(Throwable t) throws ApplicationException {
		ImportTaskVO task = null;
		if(this.context.getImportedCount() > 0){
			//否则  就是部分成功
			task = buildEndTask(ExcelImportConstants.STATUS_PARTIAL_IMPORTED);
		}else{
			task = buildEndTask(ExcelImportConstants.STATUS_EXCEPTION);
		}
		task.setErrorMsg(ExcelUtil.buildErrorMsg4Security(t));
		
		// 将错误信息写入文件
		errorFileExcelCol(task);
		
		this.importTaskService.updateImportTask(task);
	
		sendMessage();
	}

	private void flushValidationErrors(ImportTaskVO task) {
		String errTxtPath =  task.getFilePath().replace(".jlr", ".txt");
		
		ICellErrorAppender errorAppender =null;
		try {
			ApacheVFSFileObject errTxtFile = new ApacheVFSFileObject(ExcelUtil.VFS_MODULE_EXCEL,errTxtPath,FileStoreType.ARCHIVE);
			errorAppender = new FileAppender(errTxtFile);
			Iterator<ExcelCellError> iter = context.getErrors().iterator();
			while(iter.hasNext()) {
				errorAppender.append(iter.next());
			}
		} catch (IOException ioe) {
			logger.error(ioe);
			// 回写错误信息时发生异常也更新任务状态
			task.setErrorMsg(ExcelUtil.buildErrorMsg4File());
			task.setImportStatus(ExcelImportConstants.STATUS_EXCEPTION);
		}
		finally{
			if(errorAppender != null){
				errorAppender.close();
			}
			
		}
		task.setValiErrorFile(errTxtPath);
	}
	
	/**
	 * 配置错误文件的设置	
	 * @author chaowangbang
	 * @since 2013-7-2
	 * @param task
	 */
	private void errorFileExcelCol(ImportTaskVO task) {
		//全部回写
		String xlsx="xlsx";
		
		if(CollectionUtil.isNullOrEmpty(context.getErrors() )){
			return;
		}
		
		//从数据字典取默认值
		String deafultErrorDisplay=RegistryQueryUtil.getValue(BASE_PATH_EXCEL);
		
		//读取xml文件取值
		ExcelMeta excelMeta=this.context.getExcelMeta();
		String errorDisplay=excelMeta.getErrorDisplay();
		
		if(xlsx.equals(deafultErrorDisplay)){
			executeErrorWriter(task,deafultErrorDisplay);
		}else if(xlsx.equals(errorDisplay)){
			executeErrorWriter(task,errorDisplay);
		}else{
			this.flushValidationErrors(task);
		}
	}
	
	/**
	 * 执行错误回写	
	 * @author chaowangbang
	 * @since 2013-9-3
	 * @param task
	 * @param errorDisplay
	 */
	private void executeErrorWriter(ImportTaskVO task,String errorDisplay){
		String errorFile = null;
		try {
			//同一服务器下加锁同步执行,防止并发操作同一文件
			synchronized (ERROR_File) {
				ExcelXMLFileAppender appender = new ExcelXMLFileAppender(context, errorDisplay);
				errorFile = appender.appenderErrorColDate();
			}
			task.setValiErrorFile(errorFile);
		} catch (IOException e) {
			logger.error(e);
			// 回写错误信息时发生异常也更新任务状态   
			task.setErrorMsg(ExcelUtil.buildErrorMsg4File());
			task.setImportStatus(ExcelImportConstants.STATUS_EXCEPTION);
		} catch (SAXException e) {
			logger.error(e);
			// 回写错误信息时发生异常也更新任务状态
			task.setErrorMsg(ExcelUtil.buildErrorMsg4File());
			task.setImportStatus(ExcelImportConstants.STATUS_EXCEPTION);
		} catch (DocumentException e) {
			logger.error(e);
			// 回写错误信息时发生异常也更新任务状态
			task.setErrorMsg(ExcelUtil.buildErrorMsg4File());
			task.setImportStatus(ExcelImportConstants.STATUS_EXCEPTION);
		}
	}
	
	/**
	 * 执行消息
	 * @throws ApplicationException
	 * Created：2012-12-6
	 * @author chaowangbang
	 */
	private void sendMessage() throws ApplicationException{
		//如果任务id为空 那么直接取消
		if(request.getTaskId()<1){
			logger.debug("taskId is null.");
			return ;
		}
		ImportTaskVO task = importTaskService.findImportTaskById(Integer.valueOf(request.getTaskId()));
		//获取当前用户
		IUserPrincipal user =  RequestContext.getCurrent().getUser();
		try{
			//推送消息
			pushMessage(user, task);
		}finally{
			//发送邮件
			sendMail(user, task);
		}
	}
	/**
	 * 
	 *  发送邮件
	 * @throws ApplicationException
	 * Created：2012-7-20
	 * @author chaowangbang
	 */
	private void sendMail(IUserPrincipal user, ImportTaskVO task)throws ApplicationException{
		//数据字典配置的发送邮件标识		  
		if(RegistryQueryUtil.getIntegerValue("Ccay.Excel.SendMail.ImportSendMail") == 1){
			logger.debug("Registry [Ccay.Excel.SendMail.ImportSendMail] !=1. Mail will not be sent.");
			return ;
		}
//		MailVO mail = new MailVO();
//		mail.setTo(new String[]{user.getEmail()});
//		mail.setTemplateName("excel_mail_template");
//		Map<String,Serializable> bindItems = new HashMap<String,Serializable>();
//		//任务类型
//		bindItems.put("taskType", "ExcelImport");
//		//用户全名
//		bindItems.put("taskOwner", user.getUserCN());
//		bindItems.put("task", task);
//		TemplateUtil.putAppRoot(bindItems);
//		mail.setBindItems(bindItems);
//		//任务
//		defaultMailService.sendMail(mail);
	}

	/** 
	 * @param user
	 * @param task
	 */
	private void pushMessage(IUserPrincipal user, ImportTaskVO message) {
		PushMessageVO pushMessage=new PushMessageVO();
		pushMessage.setMessageType("ExcelImport");
		Map<String,Serializable> bindItems = new HashMap<String,Serializable>();
		//用户全名
		bindItems.put("messageOwner", user.getDisplayName());
		//任务类型
		bindItems.put("messageType", "ExcelImport");
		bindItems.put("message", message);
		pushMessage.setBindItems(bindItems);
		pushMessageService.sendAsyncMessage(pushMessage);
	}
	
	/** build一个共用的结束任务	
	 * @author chaowangbang
	 * @since Mar 21, 2012
	 * @param status
	 * @return
	 * @throws ApplicationException
	 */
	private ImportTaskVO buildEndTask(String status) throws ApplicationException {
		// 任务结束的时间
		long endTime = System.currentTimeMillis();
		
		// 更新任务信息
		int taskId = request.getTaskId();
		ImportTaskVO task = this.importTaskService.findImportTaskById(taskId);
		task.setImportStatus(status); // 状态
		task.setRunEndTime(new Date(endTime)); // 任务结束时间
		task.setOperateQty(this.context.getProcessedCount()); // 操作的记录数
		task.setImportedQty(this.context.getImportedCount()); // 成功导入的记录数
		
		return task;
	}
}
